package com.yfp.client.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.notify.WxPayPartnerNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayPartnerRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.WxPayPartnerOrderCloseV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.pagehelper.PageInfo;
import com.hy.corecode.idgen.WFGIdGenerator;
import com.yfp.client.config.WxPayConfiguration;
import com.yfp.client.config.WxPayProperties;
import com.yfp.client.domain.*;
import com.yfp.client.domain.dto.*;
import com.yfp.client.domain.*;
import com.yfp.client.domain.dto.*;
import com.yfp.client.domain.vo.MallOrderDetailVO;
import com.yfp.client.domain.vo.MallOrderListReqVO;
import com.yfp.client.enums.*;
import com.yfp.client.enums.MallOrderStatusEnum;
import com.yfp.client.enums.MallProductStatusEnum;
import com.yfp.client.enums.MallProductTypeEnum;
import com.yfp.client.enums.WxPayAttach;
import com.yfp.client.mapper.ClientIntegralMapper;
import com.yfp.client.mapper.MallOrderMapper;
import com.yfp.client.service.*;
import com.yfp.client.service.*;
import com.yfp.client.util.RedisDistributedLock;
import com.yfp.client.util.SecurityUtils;
import com.yfp.common.core.constant.CacheConstants;
import com.yfp.common.core.domain.LogisticsInfoVO;
import com.yfp.common.core.utils.DateUtils;
import com.yfp.common.core.utils.PageUtils;
import com.yfp.common.core.web.page.TableDataInfo;
import com.yfp.common.redis.utils.RedisDelayQueueUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.util.Asserts;
import org.jetbrains.annotations.NotNull;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.yfp.common.core.constant.CacheConstants.MINAPP_ORDER_LOGISTICS_INFO_KEY;

/**
 *
 * @description 针对表【mall_order(商城订单)】的数据库操作Service实现
 * @createDate 2025-03-05 10:54:48
 */
@Slf4j
@Service
public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder> implements MallOrderService {

    @Resource
    private WFGIdGenerator wFGIdGenerator;
    @Resource
    public RedisTemplate redisTemplate;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private RedisDelayQueueUtil redisDelayQueueUtil;
    @Resource
    private SysDictDataService sysDictDataService;
    @Resource
    private ClientService clientService;
    @Resource
    private ClientAddressService clientAddressService;
    @Resource
    private MallProductService mallProductService;
    @Resource
    private MallOrderMapper mallOrderMapper;
    @Resource
    private MallOrderDetailsService mallOrderDetailsService;
    @Resource
    private WxPayProperties properties;
    @Resource
    private PayService payService;
    @Resource
    private ClientIntegralService clientIntegralService;
    @Resource
    private ClientIntegralMapper clientIntegralMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long settle(PackSettleDTO packSettleDTO) {
        String clientId = SecurityUtils.getClientId();
        log.info("商城购买用户ID：{},商城购买内容:{}", clientId, packSettleDTO);
        // 小程序下单锁
        String lockKey = CacheConstants.MINAPP_ORDER_MALLPRODUCT_CREATE_LOCK_KEY + packSettleDTO.getComboDTOS().getId();
        RLock lock = redissonClient.getLock(lockKey);
        Assert.isTrue(lock.tryLock(), "稍后重试");
        try {
            // 校验传入数据
            Long mallProductId = packSettleDTO.getComboDTOS().getId();
            this.checkProductSettle(mallProductId);
            // 创建订单
            Long mallOrderId = this.createSettle(packSettleDTO);
            return mallOrderId;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public Boolean settleOrderCheck(Long mallOrderId) {
        MallOrder mallOrder = mallOrderMapper.selectById(mallOrderId);
        Asserts.notNull(mallOrder, "订单不存在");
        Assert.isTrue(MallOrderStatusEnum.WAIT_PAY.getCode().equals(mallOrder.getStatus()), "当前订单状态非待支付");

        List<MallOrderDetails> mallOrderDetails = mallOrderDetailsService.listByMallOrderId(mallOrderId);
        for (MallOrderDetails mallOrderDetail : mallOrderDetails) {
            MallProduct mallProduct = mallProductService.getById(mallOrderDetail.getMallProductId());
            Assert.notNull(mallProduct, "无该商品");
            Assert.isTrue(MallProductStatusEnum.ON_SHELF.getCode().equals(mallProduct.getStatus()), "当前商品已下架");
        }
        return Boolean.TRUE;
    }

    @Override
    public WxPayUnifiedOrderV3Result.JsapiResult rechargeSettlePayment(Long mallOrderId) {
        String clientId = SecurityUtils.getClientId();

        Client client = clientService.getById(clientId);
        MallOrder mallOrder = mallOrderMapper.selectById(mallOrderId);

        if (!DateUtil.isIn(mallOrder.getCreateTime(), mallOrder.getCreateTime(), DateUtil.date().offset(DateField.MINUTE, 3))) {
            this.cancelOrder(mallOrderId);
            Assert.isFalse(true, "已经超过支付时间");
        }

        // 微信支付订单、微信and积分支付订单
        if (NumberUtil.isGreater(NumberUtil.toBigDecimal(mallOrder.getPayPrice()), BigDecimal.ZERO)) {
            String subMchId = sysDictDataService.getDefaultReceivingAccount();

            // 创建微信支付订单
            CreateWxPayOrderDTO createWxPayOrderDTO = new CreateWxPayOrderDTO();
            createWxPayOrderDTO.setDescription("商城订单");
            createWxPayOrderDTO.setTimeExpire(DateUtil.date(mallOrder.getCreateTime()).offset(DateField.MINUTE, 3));
            createWxPayOrderDTO.setTotalPrice(mallOrder.getPayPrice());
            createWxPayOrderDTO.setSubMchId(subMchId);
            createWxPayOrderDTO.setAttach(WxPayAttach.MALLPRODUCT_VALUE_TYPE.getValue());
            createWxPayOrderDTO.setOpenid(client.getOpenId());
            createWxPayOrderDTO.setOutTradeNo(mallOrder.getOrderSn());

            // 调用微信支付
            WxPayUnifiedOrderV3Result.JsapiResult wxJsapiPayOrder = payService.createWxJsapiPayOrder(createWxPayOrderDTO);
            String wxPrepayId = StrUtil.replace(wxJsapiPayOrder.getPackageValue(), "prepay_id=", "");

            this.updatePrepayIdById(mallOrder.getId(), wxPrepayId);
            return wxJsapiPayOrder;
        }
        return null;
    }

    @Override
    public void rechargeSettlePaymentIntegral(Long mallOrderId) {
        MallOrder mallOrder = mallOrderMapper.selectById(mallOrderId);

        WxPayPartnerNotifyV3Result.DecryptNotifyResult decryptNotifyResult = new WxPayPartnerNotifyV3Result.DecryptNotifyResult();
        decryptNotifyResult.setOutTradeNo(mallOrder.getOrderSn());
        decryptNotifyResult.setSuccessTime(DateUtil.format(DateUtil.date(), DatePattern.UTC_WITH_XXX_OFFSET_PATTERN));
        WxPayPartnerNotifyV3Result wxPayPartnerNotifyV3Result = new WxPayPartnerNotifyV3Result();
        wxPayPartnerNotifyV3Result.setResult(decryptNotifyResult);

        this.orderCreateNotify(wxPayPartnerNotifyV3Result);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean cancelOrder(Long mallOrderId) {
        DateTime date = DateUtil.date();

        MallOrder mallOrder = mallOrderMapper.selectById(mallOrderId);

        if (!MallOrderStatusEnum.WAIT_PAY.getCode().equals(mallOrder.getStatus())) {
            return false;
        }

        // 微信关闭订单
        this.closeOrder(mallOrderId);

        // 修改订单状态
        mallOrder.setStatus(MallOrderStatusEnum.CANCEL.getCode());
        mallOrder.setUpdateTime(date);
        mallOrder.setCancelTime(date);
        mallOrderMapper.updateById(mallOrder);
        // 更新库存-加
        mallProductService.recoverStockQuantity(mallOrder);
        // 恢复积分
        clientService.recoverLockIntegralQuantity(mallOrder.getClientId(), mallOrder.getIntegral(), mallOrder.getOrderSn());
        return true;
    }

    @Override
    public Boolean confirmOrder(Long mallOrderId) {
        MallOrder mallOrder = mallOrderMapper.selectById(mallOrderId);
        mallOrderMapper.updateStatus(mallOrder.getOrderSn(), MallOrderStatusEnum.COMPLETE.getCode());
        return true;
    }

    @Override
    public void updateAddress(MallOrderAddressDTO mallOrderAddressDTO) {
        DateTime date = DateUtil.date();
        MallOrder mallOrder = mallOrderMapper.selectById(mallOrderAddressDTO.getMallOrderId());
        ClientAddress clientAddress = clientAddressService.getById(mallOrderAddressDTO.getClientAddressId());
        Assert.notNull(clientAddress, "无此收货地址");

        mallOrder.setAddress(clientAddress.getAddress());
        mallOrder.setMobile(clientAddress.getMobile());
        mallOrder.setUpdateTime(date);
        mallOrder.setClientAddressId(clientAddress.getId());
        mallOrderMapper.updateById(mallOrder);
    }

    @Override
    public void checkProductSettle(Long mallProductId) {
        Assert.notNull(mallProductId, "购买商品不能为空");
        MallProduct mallProduct = mallProductService.getById(mallProductId);
        Assert.notNull(mallProduct, "无该商品");
        Assert.isTrue(NumberUtil.equals(2, mallProduct.getStatus()), "当前商品未上架");
        Assert.isFalse(NumberUtil.equals(2, mallProduct.getStatus()) && NumberUtil.isLessOrEqual(NumberUtil.toBigDecimal(mallProduct.getCurrentInventory()), BigDecimal.ZERO), "当前商品已售罄");

    }

    @Override
    public Long createSettle(PackSettleDTO packSettleDTO) {
        String clientId = SecurityUtils.getClientId();
        DateTime date = DateUtil.date();

        Assert.notNull(packSettleDTO.getClientAddressId(), "请选择收货地址");
        Assert.notNull(packSettleDTO.getComboDTOS().getId(), "请选择购买商品");
        ClientAddress clientAddress = clientAddressService.getById(packSettleDTO.getClientAddressId());
        Assert.notNull(clientAddress, "无此收货地址");
        MallProduct mallProduct = mallProductService.getById(packSettleDTO.getComboDTOS().getId());
        Assert.notNull(mallProduct, "无此商品");

        // 封装订单主表数据
        MallOrder mallOrder = new MallOrder();
        mallOrder.setOrderSn(String.valueOf(wFGIdGenerator.next()));
        mallOrder.setPrice(mallProduct.getPrice());
        mallOrder.setStatus(MallOrderStatusEnum.WAIT_PAY.getCode());
        mallOrder.setLogisticsStatus(null);
        mallOrder.setClientId(clientId);
        mallOrder.setPayTime(null);
        mallOrder.setPrepayId(null);
        mallOrder.setPaySn(null);
        mallOrder.setPayPrice(mallProduct.getPrice());
        mallOrder.setIntegral(mallProduct.getIntegral());
        mallOrder.setLogisticsSn(null);
        mallOrder.setAddress(clientAddress.getAddress());
        mallOrder.setMobile(clientAddress.getMobile());
        mallOrder.setLogisticsName(null);
        mallOrder.setCreateTime(date);
        mallOrder.setUpdateTime(date);
        mallOrder.setCancelTime(null);
        mallOrder.setClientAddressId(clientAddress.getId());
        this.save(mallOrder);
        // 封装订单子表数据
        MallOrderDetails mallOrderDetails = new MallOrderDetails();
        mallOrderDetails.setMallOrderId(mallOrder.getId());
        mallOrderDetails.setMallProductId(mallProduct.getId());
        mallOrderDetails.setProductName(mallProduct.getProductName());
        mallOrderDetails.setPrice(mallProduct.getPrice());
        mallOrderDetails.setProductInstructions(mallProduct.getProductInstructions());
        mallOrderDetails.setBuyInstructions(mallProduct.getBuyInstructions());
        mallOrderDetails.setUseRule(mallProduct.getUseRule());
        mallOrderDetails.setProductImg(mallProduct.getProductImg());
        mallOrderDetails.setProductHeadimg(mallProduct.getProductHeadimg());
        mallOrderDetails.setProductInfoimg(mallProduct.getProductInfoimg());
        mallOrderDetails.setIntegral(mallProduct.getIntegral());
        mallOrderDetails.setMallProductType(mallProduct.getType());
        mallOrderDetailsService.save(mallOrderDetails);

        // 更新库存-减
        mallProductService.decreaseStockQuantity(packSettleDTO.getComboDTOS().getId());
        // 锁定客户积分
        if (MallProductTypeEnum.INTEGRAL.getCode().equals(mallProduct.getType()) || MallProductTypeEnum.CASH_INTEGRAL.getCode().equals(mallProduct.getType())) {
            clientService.decreaseIntegralQuantity(mallOrder.getClientId(), mallOrder.getIntegral(), mallOrder.getOrderSn());
        }

        // 将订单信息加入到延迟队列。如果超时没有支付，取消订单，恢复库存，恢复积分
        String jsonStr = JSONUtil.toJsonStr(mallOrder);
        redisDelayQueueUtil.addDelayQueue(jsonStr, 3, TimeUnit.MINUTES, CacheConstants.MINAPP_ORDER_MALLPRODUCT_CREATE_DELAY_QUEUE_KEY);
        return mallOrder.getId();
    }

    @Override
    public void updatePrepayIdById(Long id, String wxPrepayId) {
        mallOrderMapper.updatePrepayIdById(id, wxPrepayId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void orderCreateNotify(WxPayPartnerNotifyV3Result wxPayPartnerNotifyV3Result) {
        DateTime date = DateUtil.date();
        String orderSn = wxPayPartnerNotifyV3Result.getResult().getOutTradeNo();
        RedisDistributedLock redisDistributedLock = new RedisDistributedLock(redisTemplate, "client:wxpay:orderCreateNotify:" + orderSn, 10);
        try {
            // 加锁
            redisDistributedLock.lock();

            MallOrder mallOrder = this.getByOrderSn(orderSn);
            if (Objects.isNull(mallOrder)) {
                return;
            }
            if (!MallOrderStatusEnum.WAIT_PAY.getCode().equals(mallOrder.getStatus())) {
                return;
            }

            // 修改订单状态
            mallOrder.setStatus(MallOrderStatusEnum.WAIT_SHIPMENT.getCode());
            mallOrder.setPayTime(DateUtil.parseUTC(wxPayPartnerNotifyV3Result.getResult().getSuccessTime()).toTimestamp());
            mallOrder.setPaySn(wxPayPartnerNotifyV3Result.getResult().getTransactionId());
            mallOrder.setUpdateTime(date);
            this.updateById(mallOrder);

        } catch (Exception e) {
            log.info("orderCreateNotify:出现异常， 异常信息：{}", e.getMessage());
        } finally {
            redisDistributedLock.unlock();
        }
    }

    @Override
    public MallOrder getByOrderSn(String orderSn) {
        return mallOrderMapper.getByOrderSn(orderSn);
    }

    @Override
    public TableDataInfo<MallOrderListReqVO> getClientOrderList(MallOrderListReqDTO mallOrderListReqDTO) {
        String clientId = SecurityUtils.getClientId();
        // 前端入参             为 null全部、1待支付、2待收货、3已完成、4已退款and已取消
        // 实际数据库表字段定义   为 1待支付、2待收货、3已完成、4已退款、5已取消、6退款中、7待发货

        List<Integer> statusList = null;
        if (Objects.nonNull(mallOrderListReqDTO.getStatus())) {
            statusList = new ArrayList<>();
            statusList.add(mallOrderListReqDTO.getStatus());
            if (MallOrderStatusEnum.REFUND.getCode().equals(mallOrderListReqDTO.getStatus())) {
                statusList.add(MallOrderStatusEnum.REFUND.getCode());
                statusList.add(MallOrderStatusEnum.CANCEL.getCode());
                statusList.add(MallOrderStatusEnum.SALES_RETURN_AND_REFUND.getCode());
            } else if (MallOrderStatusEnum.WAIT_RECEIVE.getCode().equals(mallOrderListReqDTO.getStatus())) {
                statusList.add(MallOrderStatusEnum.WAIT_RECEIVE.getCode());
                statusList.add(MallOrderStatusEnum.WAIT_SHIPMENT.getCode());
            }
        }
        PageUtils.startPage();
        List<MallOrder> mallOrders = this.listByClientIdAndStatusIds(clientId, statusList);
        long total = new PageInfo(mallOrders).getTotal();

        List<Long> mallOrderIds = mallOrders.stream().map(MallOrder::getId).toList();
        List<MallOrderDetails> mallOrderDetails = mallOrderDetailsService.listByMallOrderIds(mallOrderIds);
        Map<Long, List<MallOrderDetails>> mallOrderDetailsMapByMallOrderId = mallOrderDetails.stream().collect(Collectors.groupingBy(MallOrderDetails::getMallOrderId));
        // 转换数据格式，填充子订单数据
        List<MallOrderListReqVO> list = mallOrders.stream().map(entity -> {
            MallOrderListReqVO bean = BeanUtil.toBean(entity, MallOrderListReqVO.class);
            List<MallOrderDetails> mallOrderDetailsTemp = mallOrderDetailsMapByMallOrderId.getOrDefault(entity.getId(), List.of());
            List<MallOrderDetailVO> mallOrderDetailVOS = BeanUtil.copyToList(mallOrderDetailsTemp, MallOrderDetailVO.class);
            for (MallOrderDetailVO mallOrderDetailVO : mallOrderDetailVOS) {
                mallOrderDetailVO.setBuyNum(1);
            }
            bean.setMallOrderDetailsList(mallOrderDetailVOS);
            return bean;
        }).toList();

        TableDataInfo<MallOrderListReqVO> dataTable = PageUtils.getDataTable(list);
        dataTable.setTotal(total);
        return dataTable;
    }

    @Override
    public List<MallOrder> listByClientIdAndStatusIds(String clientId, List<Integer> statusList) {
        List<MallOrder> mallOrders = mallOrderMapper.listByClientIdAndStatusIds(clientId, statusList);
        return mallOrders;
    }

    @Override
    public MallOrderListReqVO getClientOrderInfo(Long mallOrderId) {

        MallOrder mallOrder = mallOrderMapper.selectById(mallOrderId);
        MallOrderListReqVO bean = BeanUtil.toBean(mallOrder, MallOrderListReqVO.class);

        List<MallOrderDetails> mallOrderDetails = mallOrderDetailsService.listByMallOrderId(mallOrderId);
        List<MallOrderDetailVO> mallOrderDetailVOS = BeanUtil.copyToList(mallOrderDetails, MallOrderDetailVO.class);
        for (MallOrderDetailVO mallOrderDetailVO : mallOrderDetailVOS) {
            mallOrderDetailVO.setBuyNum(1);
        }
        bean.setMallOrderDetailsList(mallOrderDetailVOS);

        ClientAddress clientAddress = clientAddressService.getById(mallOrder.getClientAddressId());
        bean.setClientAddress(clientAddress);
        return bean;
    }

    @Override
    public void closeOrder(Long mallOrderId) {
        log.info("商城支付，关闭订单,orderId:{}", mallOrderId);
        MallOrder mallOrder = mallOrderMapper.selectById(mallOrderId);
        String subMchId = sysDictDataService.getDefaultReceivingAccount();

        WxPayService mxPayService = WxPayConfiguration.getMxPayService(properties.getConfigs().get(1));
        WxPayPartnerOrderCloseV3Request wxPayPartnerOrderCloseV3Request = new WxPayPartnerOrderCloseV3Request();
        wxPayPartnerOrderCloseV3Request.setOutTradeNo(mallOrder.getOrderSn());
        wxPayPartnerOrderCloseV3Request.setSpMchId(properties.getConfigs().get(1).getMchId());
        wxPayPartnerOrderCloseV3Request.setSubMchId(subMchId);
        try {
            mxPayService.closePartnerOrderV3(wxPayPartnerOrderCloseV3Request);
        } catch (WxPayException e) {
            log.error("关闭订单失败,orderId:{}", mallOrderId, e);
        }
    }

    @Override
    public LogisticsInfoVO getOrderLogisticsInfo(Long mallOrderId) {
        LogisticsInfoVO logisticsInfoVOTemp = (LogisticsInfoVO) redisTemplate.opsForValue().get(MINAPP_ORDER_LOGISTICS_INFO_KEY + mallOrderId);
        if (Objects.nonNull(logisticsInfoVOTemp)) {
            return logisticsInfoVOTemp;
        }

        MallOrder mallOrder = mallOrderMapper.selectById(mallOrderId);

        LogisticsInfoDTO logisticsInfoDTO = this.getLogisticsInfoDTO(mallOrderId, mallOrder.getLogisticsSn(), mallOrder.getMobile());
        // 返回的订单号可能会出现拼接手机尾号的情况，重新赋值一下
        logisticsInfoDTO.getResult().setNumber(mallOrder.getLogisticsSn());

        LogisticsInfoVO logisticsInfoVO = BeanUtil.toBean(logisticsInfoDTO.getResult(), LogisticsInfoVO.class);
        redisTemplate.opsForValue().set(MINAPP_ORDER_LOGISTICS_INFO_KEY + mallOrderId, logisticsInfoVO,1 , TimeUnit.DAYS);
        return logisticsInfoVO;
    }

    @NotNull
    private LogisticsInfoDTO getLogisticsInfoDTO(Long mallOrderId, String logisticsSn, String mobile) {
        HttpResponse execute = HttpUtil.createGet("https://wuliu.market.alicloudapi.com/kdi")
                .header(Header.AUTHORIZATION, "APPCODE " + "da81a8042ee741f181145252c509e5c6")
                .form("no", logisticsSn + ":" + PhoneUtil.subAfter(mobile))
                .execute();

        if (!execute.isOk()) {
            int httpCode = execute.getStatus();
            Map<String, List<String>> headers = execute.headers();
            String error = headers.get("X-Ca-Error-Message").get(0);
            if (httpCode == 400 && "Invalid AppCode `not exists`".equals(error)) {
                throw new RuntimeException("物流信息查询失败：" + "AppCode错误");
            } else if (httpCode == 400 && "Invalid Url".equals(error)) {
                throw new RuntimeException("物流信息查询失败：" + "请求的 Method、Path 或者环境错误");
            } else if (httpCode == 400 && "Invalid Param Location".equals(error)) {
                throw new RuntimeException("物流信息查询失败：" + "参数错误");
            } else if (httpCode == 403 && "Unauthorized".equals(error)) {
                throw new RuntimeException("物流信息查询失败：" + "服务未被授权（或URL和Path不正确）");
            } else if (httpCode == 403 && "Quota Exhausted".equals(error)) {
                throw new RuntimeException("物流信息查询失败：" + "套餐包次数用完");
            } else if (httpCode == 403 && "Api Market Subscription quota exhausted".equals(error)) {
                throw new RuntimeException("物流信息查询失败：" + "套餐包次数用完，请续购套餐");
            } else {
                throw new RuntimeException("物流信息查询失败：" + "参数名错误 或 其他错误");
            }
        }

        LogisticsInfoDTO logisticsInfoDTO = JSONUtil.toBean(execute.body(), LogisticsInfoDTO.class);
        log.info("物流信息查询成功,orderId:{},status:{},msg:{}", mallOrderId, logisticsInfoDTO.getStatus(), logisticsInfoDTO.getMsg());
        return logisticsInfoDTO;
    }

    @Override
    public void updateOrderLogisticsInfoJob() {
        LambdaQueryWrapper<MallOrder> query = Wrappers.lambdaQuery(MallOrder.class);
        query.in(MallOrder::getStatus, MallOrderStatusEnum.WAIT_RECEIVE.getCode());
        query.isNotNull(MallOrder::getLogisticsSn);
        List<MallOrder> mallOrders = mallOrderMapper.selectList(query);

        for (MallOrder mallOrder : mallOrders) {
            LogisticsInfoDTO logisticsInfoDTO = this.getLogisticsInfoDTO(mallOrder.getId(), mallOrder.getLogisticsSn(), mallOrder.getMobile());
            LogisticsInfoVO logisticsInfoVO = BeanUtil.toBean(logisticsInfoDTO.getResult(), LogisticsInfoVO.class);
            redisTemplate.opsForValue().set(MINAPP_ORDER_LOGISTICS_INFO_KEY + mallOrder.getId(), logisticsInfoVO,6 , TimeUnit.HOURS);
            // 当物流状态为签收时，校验签收时间是否超过7天，超过则订单状态为已完成
            if (!StrUtil.isEmpty(logisticsInfoDTO.getResult().getDeliverystatus()) && logisticsInfoDTO.getResult().getDeliverystatus().equals("3")) {
                // 签收时间
                String time = logisticsInfoDTO.getResult().getList().get(0).getTime();
                boolean exceedSevenDay = DateUtils.isExceedSevenDay(time);
                if (exceedSevenDay) {
                    mallOrder.setStatus(MallOrderStatusEnum.COMPLETE.getCode());
                }
            }
            if (!StrUtil.isEmpty(logisticsInfoDTO.getResult().getDeliverystatus())) {
                mallOrder.setLogisticsStatus(Integer.valueOf(logisticsInfoDTO.getResult().getDeliverystatus()));
                mallOrder.setUpdateTime(DateUtil.date());
                mallOrderMapper.updateById(mallOrder);
            }
        }
    }

    /**
     * 修改订单状态
     *
     * @param orderSn
     * @param status
     */
    @Override
    public void updateStatus(String orderSn, Integer status) {
        mallOrderMapper.updateStatus(orderSn, status);
    }

    /**
     * 商城订单退款回调
     *
     * @param wxPayPartnerRefundNotifyV3Result
     */
    @Override
    @Transactional
    public void mallOrderRefundNotify(WxPayPartnerRefundNotifyV3Result wxPayPartnerRefundNotifyV3Result) {

        log.info("回调处理客户信息:{}", wxPayPartnerRefundNotifyV3Result);
        String orderSn = wxPayPartnerRefundNotifyV3Result.getResult().getOutTradeNo();

        // 根据订单号获取订单详情信息
        MallOrderDetails mod = mallOrderMapper.orderSnGetDetail(orderSn);
        MallOrder mo = mallOrderMapper.getByOrderSn(orderSn);

        // 判断是否需要退积分
        if (mod.getMallProductType().equals(MallProductTypeEnum.CASH_INTEGRAL.getCode())) {
            RLock lock = redissonClient.getLock(CacheConstants.CLIENT_INTEGRAL_LOCK_KEY + mo.getClientId());
            try {
                boolean b = lock.tryLock(5, TimeUnit.SECONDS);
                Assert.isTrue(b, "稍后重试");
                clientService.returnIntegral(mo.getClientId(), mod.getIntegral());

                // 插入退款记录
                Client client = clientService.getById(mo.getClientId());
                ClientIntegral clientIntegral = new ClientIntegral();
                clientIntegral.setClientId(client.getId());
                clientIntegral.setIntegral(mod.getIntegral().intValue());
                clientIntegral.setSurplusIntegral(client.getIntegral());
                clientIntegral.setStatus(2);
                clientIntegral.setSource(2);
                clientIntegral.setType(1);
                clientIntegral.setOrderId(mo.getOrderSn());
                clientIntegralMapper.insert(clientIntegral);

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 释放锁
                lock.unlock();
            }
        }

        Integer status = MallOrderStatusEnum.REFUND.getCode();
        // 没有物流状态，退还库存
        if (mo.getLogisticsStatus() == null) {
            // 状态退货退款
            status = MallOrderStatusEnum.SALES_RETURN_AND_REFUND.getCode();
            // 退库存
            mallProductService.recoverStockQuantity(mo);
        }
        // 修改订单状态
        mallOrderMapper.updateStatus(orderSn, status);

    }

}




